//
//  MMGSocketClient.m
//  MyApp
//
//  Created by Don on 2017/8/28.
//  Copyright © 2017年 com.memego All rights reserved.
//

#import "MMGSocketClient.h"
#import <HWWeakTimer.h>
#import <CocoaAsyncSocket/GCDAsyncSocket.h>
#import "MMGSocketClient+MessageSender.h"
#import "NSDictionary+JsonString2Dic.h"
#import "MMGSocketMsgModel.h"
#import "NSDate+MMGTimestamp.h"

static int const Heart_Beat_Interval = 2;
static int const Heart_Beat_TimeOut_Interval = 18;
//static int const kSocket_MaxReconnection_time = 5;

@interface MMGSocketClient ()<GCDAsyncSocketDelegate>
@property (nonatomic, strong) NSString *host;
@property (nonatomic, assign) uint16_t port;

@property (nonatomic, assign) BOOL isConnected;
@property (nonatomic, weak) NSTimer *heartbeatTimer;
@property (nonatomic, weak) NSTimer *sendHeartbeatTimeOutTimer;
@property (nonatomic, strong) GCDAsyncSocket *clientSocket;

@property (nonatomic, strong) NSMutableData *rvcBuffer;//socket 接受数据缓存
@property (nonatomic, assign) BOOL need2ExitSendingThread;

@property (nonatomic, assign) int reconnection_time;
@property (nonatomic, weak) NSTimer *reconnecttionTimer;

@property (nonatomic, assign)long long lastSendingDataTimestamp;//最后一个已发包的发包时间
@end
@implementation MMGSocketClient {
    dispatch_queue_t socketQueue;
}

+ (instancetype)shareMMGSocketClient {
    static MMGSocketClient *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[MMGSocketClient alloc] init];
    });
    return instance;
}
- (instancetype)init {
    if ((self = [super init])) {
        socketQueue = dispatch_queue_create("com.memego.socket.queue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)connectServerHost:(NSString *)host port:(uint16_t)port {
    NSAssert(host.length != 0, @"Invalid host: Cannot connect : until host is not nil. ");
    self.host = host;
    self.port = port;
    
    NSError *error = nil;
    [self.clientSocket connectToHost:host onPort:port error:&error];
    if (error) {
        DLog(@"host:%@ port:%u  connect error :%@",host,port,error.localizedDescription);
        [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidFailWithError object:error];
    }
}

- (void)reconnection {
    [self stopHeartbeatTimer];
    [self stopHeartbeatTimeOutTimer];
    self.isConnected = NO;
    //清空数据接收缓存
    _rvcBuffer = nil;
    DLog(@"reconnection host:%@ , port:%hu",self.host,self.port);
    [self connectServerHost:self.host port:self.port];
}
- (void)closeSocket {
    
    if (_clientSocket) {
        [_clientSocket disconnect];
        [_clientSocket setDelegate:nil];
        _clientSocket = nil;
    }
    //KILL SENDING THREAD
//    self.need2ExitSendingThread = YES;
    [self stopHeartbeatTimer];
//    [self stopHeartbeatTimeOutTimer];
    self.isConnected = NO;
    //清空数据接收缓存
    _rvcBuffer = nil;
//    [self.sendingDataBuffer removeAllObjects];
}
- (BOOL)socketIsConnected {
    
    if (_clientSocket) {
        DLog(@"socket host:%@ port:%u is connected %d",self.clientSocket.connectedHost,self.clientSocket.connectedPort,self.clientSocket.isConnected);
        return _clientSocket.isConnected;
    }
    return NO;
}
- (void)sendMessage:(NSData *)msg {
    [self.clientSocket writeData:msg withTimeout:-1 tag:0];
    
}
#pragma mark - socket delegate
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
//    [sock readDataWithTimeout:-1 tag:0];
    // 连接后,可读取服务端的数据
    [sock readDataToData:[GCDAsyncSocket ZeroData] withTimeout:200 tag:0];
    self.isConnected = YES;
    [self startHeartbeatTimer];
//    self.need2ExitSendingThread = NO;
//    dispatch_async(dispatch_get_global_queue(0, DISPATCH_QUEUE_SERIAL), ^{
//        [self sendingDataThread];
//    });
    
    [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidOpen object:nil];
    
    
}

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    [sock readDataToData:[GCDAsyncSocket ZeroData] withTimeout:200 tag:0];
}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
//    [sock readDataWithTimeout:-1 tag:0];
    
    if (data.length > 0 ) {
        
        [self.rvcBuffer appendBytes:[data bytes] length:data.length];
        DLog(@"did recivee .. buffer length %lu",(unsigned long)self.rvcBuffer.length);
        if (self.rvcBuffer.length >= 4) {
            
            uint8_t* p = (uint8_t*)[self.rvcBuffer bytes];
            int len;
            len = (((int)p[0]) << 24) | (((int)p[1]) << 16) | (((int)p[2]) << 8) | (int)p[3];
            DLog(@"curretn len :%d",len);
            if ((len +4) <= self.rvcBuffer.length) {
                NSRange jsonDataRange = (NSRange){4,len};
                NSData *_data = [self.rvcBuffer subdataWithRange:jsonDataRange];
                NSString *recieveStr = [[NSString alloc] initWithBytes:[_data bytes] length:_data.length encoding:NSUTF8StringEncoding];
                
                unsigned long ramainLength = self.rvcBuffer.length - (len +4);
                if (ramainLength > 0) {
                    NSData *remainData = [self.rvcBuffer subdataWithRange:(NSRange){(len+4),ramainLength}];
                    [self.rvcBuffer setData:remainData];
                }else {
                    _rvcBuffer = nil;
                }
                
                DLog(@"--------------------did recieve string %@", recieveStr);
                NSRange range = [recieveStr rangeOfString:@"{"];
                if (range.location != NSNotFound) {
                    NSString *jsonStr = [recieveStr substringFromIndex:range.location];
                    NSDictionary *jsDic = [NSDictionary dictionaryWithJsonString:jsonStr];
                    DLog(@"---did recieve dictionary %@",jsDic);
                    MMGSocketMsgModel *msg = [MMGSocketMsgModel modelFromDataJson:jsDic];
                    if (msg.type == EMMMGSocketMsgAckTypeSyncTime) {
                        [self updateServerTime:msg.timestamp];
                    }
                    
                    //心跳续命，只要能收到消息都代表着客户端还活着，心跳超时监听必须在未来 Heart_Beat_TimeOut_Interval 后再触发
                    //        [self heartbeatTimerTimeOutContinue];
                    
                    [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidReceiveMsg object:msg];
                }
                
            }
        }
        // 连接后,可读取服务端的数据
        [sock readDataToData:[GCDAsyncSocket ZeroData] withTimeout:200 tag:0];
        
    }
    
}
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    
    DLog(@"???????????????????????????????  socket did disconnect .....");
    [self reconnection];
//    if(_reconnection_time>=0 && _reconnection_time <= kSocket_MaxReconnection_time) {
//        
//        [_reconnecttionTimer invalidate];
//        _reconnecttionTimer=nil;
//        [self reconnection];
////        int time =pow(1,_reconnection_time);
////        _reconnecttionTimer= [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(reconnection) userInfo:nil repeats:NO];
//        
//        _reconnection_time++;
//        DLog(@"socket did reconnection,after %d try again",_reconnection_time);
//    }else{
//        
//        _reconnection_time=0;
//        DLog(@"socket Did Disconnect:%p withError: %@", sock, err);
//        [self closeSocket];
//        [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidClose object:nil];
//    }

//    [self closeSocket];
//    [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketDidClose object:nil];
    
}

#pragma mark - private m
- (void)stopHeartbeatTimeOutTimer {
    if (_sendHeartbeatTimeOutTimer) {
        [_sendHeartbeatTimeOutTimer invalidate];
        _sendHeartbeatTimeOutTimer = nil;
    }
}
- (void)stopHeartbeatTimer {
    if (_heartbeatTimer) {
        [_heartbeatTimer invalidate];
        _heartbeatTimer = nil;
    }
}
- (void)startHeartbeatTimer {
    
    dispatch_async(dispatch_get_main_queue(), ^{
        if (![_heartbeatTimer isValid])
        {
            [self.heartbeatTimer fire];
        }
    });
    
}

- (void)heartbeatTimerTimeOutContinue {
    NSDate *date = [NSDate date];
    self.sendHeartbeatTimeOutTimer.fireDate = [NSDate dateWithTimeInterval:Heart_Beat_TimeOut_Interval sinceDate:date];
    DLog(@"心跳超时日期 %@ --------- 下一次心跳时间",self.sendHeartbeatTimeOutTimer.fireDate);
}

//TODO: - 校对服务器时间
- (void)updateServerTime:(long long)timestamp {
    NSTimeInterval timeInteval = timestamp - [NSDate mmg_currentTimeStamp];
    self.syncTimestampDifference = timeInteval;
}

//TODO: - 发送数据队列（包含心跳）
- (void)sendingDataThread {
    
    do {
        
        long long currentTimestamp = [NSDate mmg_currentTimeStamp];
        if (self.sendingDataBuffer.count > 0) {
            NSLock *lock = [[NSLock alloc] init];
            [lock lock];
            
            NSData *sendingData = self.sendingDataBuffer.firstObject;
            if (sendingData != nil) {
                [self sendMessage:sendingData];
                if ([self.sendingDataBuffer containsObject:sendingData]) {
                     [self.sendingDataBuffer removeObject:sendingData];
                }
               
                self.lastSendingDataTimestamp = currentTimestamp;
            }
            
            
            [lock unlock];
        }
        else {
            
            if (currentTimestamp - self.lastSendingDataTimestamp > Heart_Beat_Interval*1000) {
                [self.clientSocket writeData:[self heartBeatMsg] withTimeout:-1 tag:0];
                self.lastSendingDataTimestamp = currentTimestamp;
            }
//            sleep(Heart_Beat_Interval);
//            [self.clientSocket writeData:[self heartBeatMsg] withTimeout:-1 tag:0];
        }
        
        
        
    } while (!self.need2ExitSendingThread);

}

#pragma mark - lazy init
- (GCDAsyncSocket *)clientSocket {
    if (!_clientSocket) {
        _clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:socketQueue];
    }
    
    return _clientSocket;
}

- (NSTimer *)heartbeatTimer {
    if (!_heartbeatTimer) {
        _heartbeatTimer = [HWWeakTimer scheduledTimerWithTimeInterval:Heart_Beat_Interval block:^(id userInfo) {
            //handle heart
            [self.clientSocket writeData:[self heartBeatMsg] withTimeout:-1 tag:0];
            
        } userInfo:nil repeats:YES];
        //此处把timer 添加到当前RUNLOOP 的NSRunLoopCommonModes模式，以防止scrollview 滚动影响
        [[NSRunLoop currentRunLoop] addTimer:_heartbeatTimer forMode:NSRunLoopCommonModes];
    }
    
    return _heartbeatTimer;
}
- (NSTimer *)sendHeartbeatTimeOutTimer {
    if (!_sendHeartbeatTimeOutTimer) {
        _sendHeartbeatTimeOutTimer = [HWWeakTimer scheduledTimerWithTimeInterval:Heart_Beat_TimeOut_Interval block:^(id userInfo) {
            //handle heartbeat time out ..
            DLog(@"心跳超时 --------------------      ");
            [[NSNotificationCenter defaultCenter]postNotificationName:kMMG_Notification_SocketHeartOutTime object:nil];
            [self closeSocket];
            
        } userInfo:nil repeats:NO];
        //此处把timer 添加到当前RUNLOOP 的NSRunLoopCommonModes模式，以防止scrollview 滚动影响
        [[NSRunLoop currentRunLoop] addTimer:_sendHeartbeatTimeOutTimer forMode:NSRunLoopCommonModes];
    }
    
    return _sendHeartbeatTimeOutTimer;
}

- (NSMutableData *)rvcBuffer {
    if (!_rvcBuffer) {
        _rvcBuffer = [[NSMutableData alloc] init];
    }
    
    return _rvcBuffer;
}
- (NSMutableArray *)sendingDataBuffer {
    if (!_sendingDataBuffer) {
        _sendingDataBuffer = [[NSMutableArray alloc] init];
    }
    return _sendingDataBuffer;
}

@end
